//
//  MTAssetsLibrary.swift
//  TA
//
//  Created by M on 2019/12/11.
//  Copyright © 2019 m. All rights reserved.
//

import UIKit
import Photos

// MARK: ------------------ 回调定义 ----------------------
/// 单个相册回调
public typealias AlbumRequestCompletion = (_ album: MTAssetAlbum) -> Swift.Void
/// 多个相册回调
public typealias AlbumsRequestCompletion = (_ albums: [MTAssetAlbum]) -> Swift.Void
/// 多媒体资源回调
public typealias AssetsRequestCompletion = (_ assets: [MTAsset]) -> Swift.Void

/// 图片完整回调
public typealias AssetLibraryImageCompletion = (_ image: UIImage?, _ info: Dictionary<AnyHashable, Any>?, _ isDegraded: Bool?) -> Swift.Void

/// 视频路径回调
public typealias AssetLibraryVideoCacheCompletion = (_ progress: Float, _ path: String?) -> Swift.Void
/// 视频资源回调
public typealias AssetLibraryVideoAssetCompletion = (_ asset: AVURLAsset) -> Swift.Void

/// 进度回调
public typealias SimpleAssetLibraryProgressHandler = (_ progress: Double) -> Swift.Void
/// 进度完整回调
public typealias AssetLibraryProgressHandler = (_ progress: Double, _ error: Error?, _ stop: UnsafeMutablePointer<ObjCBool>, _ info: Dictionary<AnyHashable, Any>?) -> Swift.Void

/// 错误回调
public typealias AssetLibraryErrorHandler = (_ error: Error?) -> Swift.Void
/// 视频错误回调
public typealias AssetLibraryVideoErrorHandler = (_ error: String?) -> Swift.Void

/// 成功 Flag 回调
public typealias AssetLibrarySuccessfulFlagHandler = (_ flag: Bool) -> Swift.Void

// MARK: ------------------ 资源管理器设置 ----------------------
public class MTAssetLibraryOption: NSObject {
    
    /// 对资源按修改时间进行排序：默认为  NO。YES 为升序。如果设置为 NO，较新的资源会显示在前面
    public var sortAscendingByModificationDate: Bool = false
    
    /// 是否可以选择图片
    public var isPhotoPickingAllowed: Bool = true
    
    /// 是否可以选择视频
    public var isVideoPickingAllowed: Bool = true
    
    /// 是否允许网络请求 - iCloud
    public var isNetworkAccessEnable: Bool = true
    
}

// MARK: ------------------ 资源管理器 ----------------------
public class MTAssetLibrary: NSObject {
    
    private override init() {
        self.option = MTAssetLibraryOption()
        super.init()
    }
    public static let `default`: MTAssetLibrary = MTAssetLibrary()
    
    /// 资源管理器设置
    public var option: MTAssetLibraryOption
    
    /// 自定义资源管理器设置
    public init(option: MTAssetLibraryOption) {
        self.option = option
        super.init()
    }
    
}

// MARK: ------------------ 相册获取 ----------------------
extension MTAssetLibrary {
    
    /// 资源管理器请求类型
    @objc
    public enum RequestType: Int {
        case image = 0
        case video
        case all
    }
    
    /// 获取相机胶卷相册资源
    public func requestForCameraRollAlbum(completion: @escaping AlbumRequestCompletion) {
        /// 设置
        let fetchOption = PHFetchOptions()
        setAccessRules(for: fetchOption)
        /// 获取相册
        let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: nil)
        /// 遍历相册 collection 返回相机胶卷相册
        smartAlbums.enumerateObjects { [weak self] (collection, index, stop) in
            guard let weakSelf = self else { return }
            if weakSelf.isCameraRoll(collection) {
                let fetchResult = PHAsset.fetchAssets(in: collection, options: fetchOption)
                let album = MTAssetAlbum(name: collection.localizedTitle, result: fetchResult, collectionSubtype: collection.assetCollectionSubtype)
                album.assets = weakSelf.generateAssets(from: fetchResult)
                album.isCameraRoll = true
                completion(album)
            }
        }
    }
    
    /// 获取视频相册
    public func requestForVideoAlbum(completion: @escaping AlbumRequestCompletion) {
        /// 设置
        let fetchOption = PHFetchOptions()
        setAccessRules(for: fetchOption)
        
        let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumVideos, options: nil)
        smartAlbums.enumerateObjects { [weak self] (collection, index, stop) in
            guard let weakSelf = self else { return }
            let fetchResult = PHAsset.fetchAssets(in: collection, options: fetchOption)
            let album = MTAssetAlbum(name: collection.localizedTitle, result: fetchResult, collectionSubtype: collection.assetCollectionSubtype)
            album.assets = weakSelf.generateAssets(from: fetchResult)
            album.isCameraRoll = true
            completion(album)
        }
    }
    
    /// 获取所有相册
    public func requestForAllAlbums(completion: @escaping AlbumsRequestCompletion) {
        /// 设置
        let fetchOption = PHFetchOptions()
        setAccessRules(for: fetchOption)
        
        let myPhotoStreamAlbum = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumMyPhotoStream, options: nil)
        let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: nil)
        let topLevelUserCollections = PHCollectionList.fetchTopLevelUserCollections(with: nil)
        let syncedAlbums = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumSyncedAlbum, options: nil)
        let sharedAlbums = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumCloudShared, options: nil)
        
        let all_ablums = [myPhotoStreamAlbum, smartAlbums, topLevelUserCollections, syncedAlbums, sharedAlbums]
        
        var albumArray: [MTAssetAlbum] = []
        
        for object in all_ablums {
            guard let fetchResults = object as? PHFetchResult<PHAssetCollection> else {
                continue
            }
            for i in 0..<fetchResults.count {
                
                let collection = fetchResults[i]
                if !(collection.isKind(of: PHAssetCollection.self)) { continue }
                let isCameraRoll = self.isCameraRoll(collection)
                if collection.estimatedAssetCount <= 0 && !isCameraRoll { continue }
                let fetchResult = PHAsset.fetchAssets(in: collection, options: fetchOption)
                if fetchResult.count < 1 && !isCameraRoll { continue }
                if collection.assetCollectionSubtype == .smartAlbumAllHidden { continue }
                if collection.assetCollectionSubtype.rawValue == 1000000201 { continue }
                
                /// 创建相册
                let album = MTAssetAlbum(name: collection.localizedTitle, result: fetchResult, collectionSubtype: collection.assetCollectionSubtype)
                if isCameraRoll || (collection.assetCollectionSubtype == .smartAlbumRecentlyAdded && albumArray.count > 2) {
                    album.isCameraRoll = true
                    var idx: Int = 0
                    if collection.assetCollectionSubtype == .smartAlbumRecentlyAdded && albumArray.count > 2 { idx = 1 }
                    albumArray.insert(album, at: idx)
                } else {
                    albumArray.append(album)
                }
            }
        }
    }
    
    /// 根据请求类型获取对应回调
    public func request(withType type: RequestType, completion: @escaping AssetsRequestCompletion) {
        
        let option = PHFetchOptions()
        option.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: self.option.sortAscendingByModificationDate)]
        
        var results: PHFetchResult<PHAsset>
        
        switch type {
            
            /// 图片
        case .image:
            results = PHAsset.fetchAssets(with: .image, options: option)
            break
            
            /// 视频
        case .video:
            results = PHAsset.fetchAssets(with: .video, options: option)
            break
            
            /// 所有
        case .all:
            let collection = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: nil)
            var maxCollection: (index: Int, count: Int) = (0, 0)
            collection.enumerateObjects { (c, index, stop) in
                let result = PHAsset.fetchAssets(in: c, options: option)
                if result.count > maxCollection.count {
                    maxCollection = (index, result.count)
                }
            }
            results = PHAsset.fetchAssets(in: collection[maxCollection.index], options: option)
            break
        }
        request(with: results, completion: completion)
    }
    
}

// MARK: ------------------ 图片获取方法 ----------------------
extension MTAssetLibrary {
    
    /// 请求图片
    @discardableResult
    public func requestImage(withAsset asset: PHAsset,
                             photoWidth: CGFloat = UIScreen.main.bounds.width / 2,
                             progressHandler: SimpleAssetLibraryProgressHandler? = nil,
                             errorHandler: AssetLibraryErrorHandler? = nil,
                             completion: @escaping AssetLibraryImageCompletion) -> Int32 {
        
        /// 计算图片大小
        let screenScale: CGFloat = 2.0
        let aspectRatio: CGFloat = CGFloat(asset.pixelWidth) / CGFloat(asset.pixelHeight)
        var pixelWidth = photoWidth * screenScale
        /// 超宽图片
        if aspectRatio > 1.8 { pixelWidth = pixelWidth * aspectRatio }
        /// 超高图片
        else if aspectRatio < 0.2 { pixelWidth = pixelWidth * 0.5 }
        let pixelHeight = pixelWidth / aspectRatio
        /// 图片请求大小
        let targetSize = CGSize(width: pixelWidth, height: pixelHeight)
        
        let offline_option = PHImageRequestOptions()
        offline_option.resizeMode = .fast
        
        return PHCachingImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: offline_option) { [weak self] (result, info) in
            
            if let result = result, let info = info {
                let isCancelled: Bool? = info[PHImageCancelledKey] as? Bool
                let isError: Bool? = info[PHImageErrorKey] as? Bool
                
                if (isCancelled == nil || !isCancelled!) && (isError == nil || !isError!) {
                    completion(result, info, info[PHImageResultIsDegradedKey] as? Bool)
                }
            }
            
            guard let isInCloud = info?[PHImageResultIsInCloudKey] as? Bool, let weakSelf = self else { return }
            
            if isInCloud && weakSelf.option.isNetworkAccessEnable {
                /// 网络请求 Option
                let networkAccess_option = PHImageRequestOptions()
                networkAccess_option.progressHandler = { (progress, error, stop, info) in
                    if let handler = progressHandler {
                        handler(progress)
                    }
                }
                networkAccess_option.isNetworkAccessAllowed = true
                networkAccess_option.resizeMode = .fast
                
                PHCachingImageManager.default().requestImageData(for: asset, options: networkAccess_option) { (imageData, dataUTI, orientation, info) in
                    if let errorKey = info?[PHImageErrorKey], let errorHandler = errorHandler {
                        errorHandler(errorKey as? Error)
                    }
                    if let data = imageData, var resultImage = UIImage(data: data, scale: 1) {
                        resultImage = resultImage.resize(to: targetSize)
                        completion(resultImage, info, false)
                    }
                }
            }
        }
    }
    
    /// 获取原图
    @discardableResult
    public func requestOriginalImage(withAsset asset: PHAsset,
                                     isSynchronous: Bool = true,
                                     completion: @escaping AssetLibraryImageCompletion) -> Int32 {
        let option = PHImageRequestOptions()
        option.isNetworkAccessAllowed = true
        option.resizeMode = .fast
        option.isSynchronous = isSynchronous
        return PHCachingImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: option) { (result, info) in
            guard let info = info else { return }
            let downloadFinined = !(info[PHImageCancelledKey] != nil) && !(info[PHImageErrorKey] != nil)
            if let image = result, downloadFinined {
                let isDegraded = info[PHImageResultIsDegradedKey] as? Bool
                completion(image, info, isDegraded)
            }
        }
    }
    
    /// 获取相册封面图
    public func requestAlbumCoverImage(withAlbum album: MTAssetAlbum?,
                                       coverSize: CGFloat = 100,
                                       errorHandler: AssetLibraryErrorHandler? = nil,
                                       completion: @escaping AssetLibraryImageCompletion) {
        guard let asset = album?.result.firstObject else { return }
        requestImage(withAsset: asset, photoWidth: coverSize, errorHandler: errorHandler, completion: completion)
    }
    
}

// MARK: ------------------ 视频获取方法 ----------------------
extension MTAssetLibrary {
    
    /// 获取视频资源
    @discardableResult
    public func requestVideo(withAsset asset: PHAsset,
                             progressHandler: SimpleAssetLibraryProgressHandler? = nil,
                             errorHandler: AssetLibraryErrorHandler? = nil,
                             assetCompletion: AssetLibraryVideoAssetCompletion? = nil,
                             cacheCompletion: AssetLibraryVideoCacheCompletion? = nil) -> Int32 {
        
        let option = PHVideoRequestOptions()
        option.version = .original
        option.deliveryMode = .automatic
        
        return PHCachingImageManager.default().requestAVAsset(forVideo: asset, options: option) { [weak self] (result, audioMix, info) in
            
            if let result = result as? AVURLAsset, let info = info {
                let isCancelled: Bool? = info[PHImageCancelledKey] as? Bool
                let isError: Bool? = info[PHImageErrorKey] as? Bool

                if (isCancelled == nil || !isCancelled!) && (isError == nil || !isError!) {
                    if let assetCompletion = assetCompletion {
                        assetCompletion(result)
                    }
                    if let cacheCompletion = cacheCompletion {
                        result.export(withPath: nil, errorHandler: errorHandler, completion: cacheCompletion)
                    }
                    return
                }
            }
            
            guard let isInCloud = info?[PHImageResultIsInCloudKey] as? Bool, let weakSelf = self else {
                return
            }
            
            if isInCloud && weakSelf.option.isNetworkAccessEnable {
                let networkAccess_option = PHVideoRequestOptions()
                networkAccess_option.progressHandler = { (progress, error, stop, info) in
                    if let handler = progressHandler {
                        handler(progress)
                    }
                }
                networkAccess_option.deliveryMode = .automatic
                networkAccess_option.version = .current
                networkAccess_option.isNetworkAccessAllowed = true
                
                PHCachingImageManager.default().requestAVAsset(forVideo: asset, options: networkAccess_option) { (asset, audioMix, info) in
                    if let asset = asset as? AVURLAsset {
                        if let assetCompletion = assetCompletion {
                            assetCompletion(asset)
                        }
                        if let cacheCompletion = cacheCompletion {
                            asset.export(withPath: nil, errorHandler: errorHandler, completion: cacheCompletion)
                        }
                    }
                }
            }
        }
    }
    
}

// MARK: ------------------ 通用方法 ----------------------
extension MTAssetLibrary {
    
    /// 取消请求
    public func cancelRequest(_ requestID: Int32) {
        PHCachingImageManager.default().cancelImageRequest(requestID)
    }
    
    /// 请求权限
    public class func request(photoLibraryAuthorize completion: @escaping (_ flag: Bool) -> Swift.Void) {
        PHPhotoLibrary.requestAuthorization { (status) in
            switch status {
            case .authorized:
                completion(true)
                break
            case .denied, .restricted, .notDetermined:
                completion(false)
                break
                
            default:
                completion(false)
                break
            }
        }
    }
    
}

// MARK: ------------------ 保存资源到相册 ----------------------
extension MTAssetLibrary {
    
    /// 保存图片至相册
    public func saveImage(from image: UIImage, errorHandler: AssetLibraryErrorHandler? = nil, completion: @escaping AssetLibrarySuccessfulFlagHandler) {
        PHPhotoLibrary.shared().performChanges({
            PHAssetChangeRequest.creationRequestForAsset(from: image)
        }) { (isSuccess, error) in
            if let error = error, let handler = errorHandler {
                handler(error)
            }
            completion(isSuccess)
        }
    }
    
    /// 保存视频至相册
    public func saveVideo(from path: String, errorHandler: AssetLibraryErrorHandler? = nil, completion: @escaping AssetLibrarySuccessfulFlagHandler) {
        PHPhotoLibrary.shared().performChanges({
            _ = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: path))
        }) { (isSuccess, error) in
            if let error = error, let handler = errorHandler {
                handler(error)
            }
            completion(isSuccess)
        }
    }
    
    /// 保存 Gif 至相册
    public func saveGif(from path: String, errorHandler: AssetLibraryErrorHandler? = nil, completion: @escaping AssetLibrarySuccessfulFlagHandler) {
        PHPhotoLibrary.shared().performChanges({
            let options = PHAssetResourceCreationOptions()
            let creationRequest = PHAssetCreationRequest.forAsset()
            creationRequest.addResource(with: .photo, fileURL: URL(fileURLWithPath: path), options: options)
        }) { (isSuccess, error) in
            if let error = error, let handler = errorHandler {
                handler(error)
            }
            completion(isSuccess)
        }
    }
    
}

// MARK: ------------------ 其他方法 ----------------------
fileprivate
extension MTAssetLibrary {
    
    /// 获取 PHFetchResult 集合中的数据，并转换成 MTAsset
    func request(with fetchResult: PHFetchResult<PHAsset>, completion: @escaping AssetsRequestCompletion) {
        var models: [MTAsset] = []
        for i in 0..<fetchResult.count {
            let model = MTAsset(asset: fetchResult[i])
            models.append(model)
        }
        completion(models)
    }
    
    /// 设置资源获取权限
    func setAccessRules(for fetchOption: PHFetchOptions) {
        if !self.option.isPhotoPickingAllowed { fetchOption.predicate = NSPredicate(format: "mediaType == \(PHAssetMediaType.video.rawValue)") }
        if !self.option.isVideoPickingAllowed { fetchOption.predicate = NSPredicate(format: "mediaType == \(PHAssetMediaType.image.rawValue)") }
        fetchOption.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: self.option.sortAscendingByModificationDate)]
    }
    
    /// 是否相机胶卷
    func isCameraRoll(_ metaData: PHAssetCollection) -> Bool {
        var versionString = UIDevice.current.systemVersion.replacingOccurrences(of: ".", with: "")
        if versionString.count <= 1 {
            versionString = versionString.appending("00")
        } else if versionString.count <= 2 {
            versionString = versionString.appending("0")
        }
        let version = Int(versionString) ?? 0
        if version >= 800 && version <= 802 {
            return metaData.assetCollectionSubtype == .smartAlbumRecentlyAdded
        } else {
            return metaData.assetCollectionSubtype == .smartAlbumUserLibrary
        }
    }
    
    /// 创建 MTAsset 数组
    func generateAssets(from results: PHFetchResult<PHAsset>) -> [MTAsset] {
        var assets: [MTAsset] = []
        for i in 0..<results.count {
            let asset = MTAsset(asset: results[i])
            assets.append(asset)
        }
        return assets
    }
    
}
